package com.crazymakercircle.mutithread.basic.create;

import com.crazymakercircle.util.Print;

import java.util.concurrent.atomic.AtomicInteger;

import static com.crazymakercircle.util.ThreadUtil.getCurThreadName;
import static com.crazymakercircle.util.ThreadUtil.sleepMilliSeconds;

/**
 * Created by 尼恩@疯狂创客圈.
 * 1.“逻辑和数据更好地分离”
 * 演示实例通过实现Runnable接口的方式创建线程目标类更加适合多个线程的代码逻辑去【共享计算】和【处理同一个资源】的场景。
 */
public class SalesDemo
{
    public static final int MAX_AMOUNT = 5; //商品数量

    //商店商品的销售线程，每条线程异步销售4次
    static class StoreGoods extends Thread
    {
        StoreGoods(String name)
        {
            super(name);
        }

        private int goodsAmount = MAX_AMOUNT;

        @Override
        public void run()
        {
            for (int i = 0; i <= MAX_AMOUNT; i++)
            {
                if (this.goodsAmount > 0)
                {
                    Print.cfo(getCurThreadName() + " 卖出一件，还剩："
                            + (--goodsAmount));
                    sleepMilliSeconds(10);

                }
            }
            Print.cfo(getCurThreadName() + " 运行结束.");
        }
    }

    //商场商品的target销售目标类，一个商品最多销售4次，可以多人销售
    static class MallGoods implements Runnable
    {
        //多人销售, 可能导致数据出错，使用原子数据类型保障数据安全
        private AtomicInteger goodsAmount = new AtomicInteger(MAX_AMOUNT);

        @Override
        public void run()
        {
            for (int i = 0; i <= MAX_AMOUNT; i++)
            {
                if (this.goodsAmount.get() > 0)
                {
                    Print.cfo(getCurThreadName() + " 卖出一件，还剩："
                            + (goodsAmount.decrementAndGet()));
                    sleepMilliSeconds(10);
                }
            }
            Print.cfo(getCurThreadName() + " 运行结束.");
        }
    }

    public static void main(String args[]) throws InterruptedException
    {
        /**
         * （1）通过继承Thread类实现多线程能更好地做到多个线程并发地完成各自的任务，访问各自的数据资源。
         * （2）通过实现Runnable接口实现多线程能更好地做到多个线程并发地完成同一个任务，
         * 访问同一份数据资源。多个线程的代码逻辑可以方便地访问和处理同一个共享数据资源（如例子中的MallGoods.goodsAmount），
         * 这样可以将线程逻辑和业务数据进行有效的分离，更好地体现了面向对象的设计思想。
         * （3）通过实现Runnable接口实现多线程时，如果数据资源存在多线程共享的情况，
         * 那么数据共享资源需要使用原子类型（而不是普通数据类型），或者需要进行线程的同步控制，
         * 以保证对共享数据操作时不会出现线程安全问题。
         */

        Print.hint("商店版本的销售");
        for (int i = 1; i <= 2; i++)
        {
            Thread thread = null;
            thread = new StoreGoods("店员-" + i);
            thread.start();
        }

        Thread.sleep(1000);
        Print.hint("商场的商品销售");
        MallGoods mallGoods = new MallGoods();
        for (int i = 1; i <= 2; i++)
        {
            Thread thread = null;
            thread = new Thread(mallGoods, "商场销售员-" + i);
            thread.start();
        }


        Print.cfo(getCurThreadName() + " 运行结束.");
    }
}